#pragma once
#ifndef OBR_RAWFILEIO_H
#define OBR_RAWFILEIO_H

#include "common.h"
#include "core/Spectrum.h"
#include "ext/miniz.h"

namespace OBR
{
namespace RawFileIO
{
template <class DataType>
static void save_file(std::string path, uint &width, uint &height, DataType *data)
{
    const uLong datasize = sizeof(DataType);
    std::ofstream file(path, std::ios::binary);
    file.write((char *)(&width), sizeof(int));
    file.write((char *)(&height), sizeof(int));

    // compress data
    uLong fullsize = datasize * width * height;
    uLong cmpsize = compressBound(fullsize);
    uchar *cmpdata = new uchar[cmpsize];
    compress(cmpdata, &cmpsize, reinterpret_cast<uchar *>(data), fullsize);
    // printf("[RAW FILE IO]: compressed raw data from %u bytes to %u bytes, rate: %.4f   \n",
    //        fullsize, cmpsize, (float)cmpsize / fullsize);

    // write compressed data
    uint64 val64;
    val64 = datasize;
    file.write((char *)(&val64), sizeof(uint64));
    val64 = fullsize;
    file.write((char *)(&val64), sizeof(uint64));
    val64 = cmpsize;
    file.write((char *)(&val64), sizeof(uint64));
    file.write((char *)cmpdata, cmpsize);

    // cleanup
    file.close();
    delete[] cmpdata;
}

template <class DataType>
static void read_file(std::string path, uint &width, uint &height, DataType **data)
{
    if (path.rfind("./", 0) == 0)
    {
        path = std::string(PROJ_ROOT) + path.substr(1, path.size() - 1);
    }

    uint64 val64;
    uLong datasize, fullsize, truesize, cmpsize;
    std::ifstream file(path, std::ios::binary);
    file.read((char *)(&width), sizeof(int));
    file.read((char *)(&height), sizeof(int));
    file.read((char *)(&val64), sizeof(int64));
    datasize = val64;
    file.read((char *)(&val64), sizeof(int64));
    fullsize = val64;
    file.read((char *)(&val64), sizeof(int64));
    cmpsize = val64;

    // check valid
    assert(datasize == sizeof(DataType));
    if (*data)
    {
        delete[] * data;
    }

    // load and uncompress
    uchar *cmpdata = new uchar[cmpsize];
    *data = new DataType[fullsize / datasize];
    file.read((char *)cmpdata, cmpsize);
    uncompress(reinterpret_cast<uchar *>(*data), &truesize, cmpdata, cmpsize);

    // check valid
    assert(truesize == fullsize);

    // cleanup
    file.close();
    delete[] cmpdata;
}
} // namespace RawFileIO
} // namespace OBR

#endif